package com.onlyxiahui.common.action.description.handler.impl;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.springframework.core.MethodParameter;

import com.onlyxiahui.common.action.description.DocumentContext;
import com.onlyxiahui.common.action.description.annotation.DocTitle;
import com.onlyxiahui.common.action.description.bean.MethodData;
import com.onlyxiahui.common.action.description.bean.ModuleData;
import com.onlyxiahui.common.action.description.bean.ParameterData;
import com.onlyxiahui.common.action.description.bean.PropertyData;
import com.onlyxiahui.common.action.description.bean.ValidateData;
import com.onlyxiahui.common.action.description.box.JsonDataTypeBox;
import com.onlyxiahui.common.action.description.enums.annotation.ParameterValidationType;
import com.onlyxiahui.common.action.description.enums.type.DocTagType;
import com.onlyxiahui.common.action.description.handler.ParameterHandler;
import com.onlyxiahui.common.action.description.handler.ParameterValidateHandler;
import com.onlyxiahui.common.action.description.handler.PropertyHandler;
import com.onlyxiahui.common.action.description.util.ActionAnnotationUtil;
import com.onlyxiahui.common.action.description.util.ActionClassUtil;
import com.onlyxiahui.common.action.description.util.ActionCommentUtil;
import com.onlyxiahui.common.action.description.util.ActionDescriptionUtil;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaParameter;

/**
 * Description <br>
 * Date 2020-01-07 17:24:27<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */

public abstract class BaseParameterHandler implements ParameterHandler {

	public ParameterData handle(
			DocumentContext context,
			ModuleData moduleData,
			MethodData methodData,
			Method method,
			MethodParameter parameter,
			String name) {

		Type parameterType = parameter.getGenericParameterType();
		Class<?> parameterClass = parameter.getParameterType();

		String type = JsonDataTypeBox.getType(parameterClass);
		String className = parameterClass.getName();
		String description = getParameterDescription(context, parameter);

		if (null == name || name.isEmpty()) {
			name = getParameterName(context, parameter);
		}

		PropertyData pd = new PropertyData();
		pd.setName(name);
		pd.setClassName(className);
		pd.setType(type);
		pd.setDescription(description);

		if (!ActionClassUtil.isPrimitive(parameterClass)) {
			List<PropertyData> nodes = null;
			for (PropertyHandler ph : context.getPropertyHandlers()) {
				if (ph.support(context, parameterClass, parameterType)) {
					nodes = ph.handle(context, parameterClass, parameterType);
				}
			}
			pd.setNodes(nodes);
		}

		for (ParameterValidateHandler h : context.getParameterValidateHandlers()) {
			if (h.support(context, pd, parameter)) {
				h.handle(context, pd, parameter);
			}
		}
		handleValidationGroups(pd, parameter);
		return new ParameterData("", pd);
	}

	public void handleValidationGroups(PropertyData pd, MethodParameter parameter) {
		ParameterValidationType[] pvts = ParameterValidationType.values();

		parameter.getParameterAnnotations();
		Map<String, List<String>> groupsMap = new HashMap<>(256);

		Annotation[] pas = parameter.getParameterAnnotations();

		for (ParameterValidationType t : pvts) {
			String validatorName = t.getValidatorName();
			String annotation = t.getAnnotation();
			String propertyName = t.getPropertyName();

			if (ActionAnnotationUtil.hasAnnotation(annotation, pas)) {
				List<String> groups = groupsMap.get(validatorName);
				if (null == groups) {
					groups = new ArrayList<String>();
					groupsMap.put(validatorName, groups);
				}

				if (null != propertyName && !propertyName.isEmpty()) {
					Object value = ActionAnnotationUtil.getValue(annotation, propertyName, pas);
					if (value instanceof String) {
						groups.add(value.toString());
					}

					if (value instanceof Object[]) {
						Object[] values = (Object[]) value;
						if (null != values && values.length > 0) {
							for (Object p : values) {
								groups.add(p.toString());
							}
						}
					}
				}
			}
		}
		handleValidationGroups(pd, groupsMap);
	}

	public void handleValidationGroups(PropertyData pd, Map<String, List<String>> groupsMap) {
		List<ValidateData> list = pd.getValidates();
		if (null != list) {
			Iterator<ValidateData> i = list.iterator();
			while (i.hasNext()) {
				boolean has = false;
				ValidateData vd = i.next();
				String validatorName = vd.getValidatorName();

				// 验证框架是否启用
				if (groupsMap.containsKey(validatorName)) {

					// TODO 当方法中不使用分组，bean的属性上验证分组，启用还是不启用有待验证
					List<String> groups = groupsMap.get(validatorName);
					List<String> validationGroups = vd.getGroups();

					boolean mark = (null != groups && !groups.isEmpty()) && (null != validationGroups && !validationGroups.isEmpty());
					if (mark) {
						for (String vg : validationGroups) {
							if (groups.contains(vg)) {
								has = true;
								break;
							}
						}
					} else {
						has = true;
					}
				}

				if (!has) {
					i.remove();
				}
			}
		}

		List<PropertyData> nodes = pd.getNodes();
		if (null != nodes) {
			for (PropertyData node : nodes) {
				handleValidationGroups(node, groupsMap);
			}
		}
	}

	public String getObjectName(Class<?> clazz) {
		String name = clazz.getSimpleName();
		if (name.length() > 1) {
			name = name.substring(0, 1).toLowerCase() + name.substring(1);
		} else {
			name = name.toLowerCase();
		}
		return name;
	}

	public String getParameterName(DocumentContext context, MethodParameter parameter) {
		Method method = parameter.getMethod();
		Class<?> moduleClass = method.getDeclaringClass();
		String moduleClassName = moduleClass.getName();
		Class<?> parameterClass = parameter.getParameterType();

		JavaMethod jm = context.getSourceBox().getJavaMethodByMethod(moduleClassName, method);

		String name = null;

		if (null != jm) {
			int index = parameter.getParameterIndex();
			List<JavaParameter> jps = jm.getParameters();
			String javaParameterName = null;
			if (null != jps) {
				if (index < jps.size()) {
					JavaParameter jp = jps.get(index);
					if (null != jp) {
						javaParameterName = jp.getName();
					}
				}
			}
			if (null == name || name.isEmpty()) {
				name = javaParameterName;
			}
		}

		if (null == name || name.isEmpty()) {
			name = parameter.getParameterName();
		}
		if (null == name || name.isEmpty()) {
			name = getObjectName(parameterClass);
		}
		return null == name || name.isEmpty() ? "" : name;
	}

	public String getParameterDescription(DocumentContext context, MethodParameter parameter) {

		Method method = parameter.getMethod();
		Class<?> moduleClass = method.getDeclaringClass();
		String moduleClassName = moduleClass.getName();
		Class<?> parameterClass = parameter.getParameterType();

		JavaMethod jm = context.getSourceBox().getJavaMethodByMethod(moduleClassName, method);
		String description = null;

		DocTitle docTitle = parameter.getParameterAnnotation(DocTitle.class);
		if (null != docTitle) {
			description = docTitle.value();
		}
		if (null == description || description.isEmpty()) {
			if (null != jm) {
				int index = parameter.getParameterIndex();
				List<JavaParameter> jps = jm.getParameters();
				String javaParameterName = null;
				if (null != jps) {
					if (index < jps.size()) {
						JavaParameter jp = jps.get(index);
						if (null != jp) {
							javaParameterName = jp.getName();
						}
					}
				}

				List<DocletTag> tags = jm.getTagsByName(DocTagType.Param.type());
				if (null != tags && index < tags.size()) {
					DocletTag docTag = tags.get(index);
					if (null != docTag) {
						String commentValue = docTag.getValue();
						if (null != commentValue) {
							if (commentValue.startsWith(javaParameterName)) {
								description = ActionCommentUtil.getParamComment(commentValue);
							} else {
								description = commentValue;
							}
						}
					}
				}
			}
		}

		if (null == description || description.isEmpty()) {
			String className = parameterClass.getName();
			JavaClass jc = context.getSourceBox().getJavaClassByName(className);
			if (null != jc) {
				description = ActionDescriptionUtil.getDescription(docTitle, jc);
			}
		}
		return null == description ? "" : description;
	}
}
